#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>

typedef void*(* func)(void*);

struct Func{
    func runner;
    void* arg;
};

struct ThreadPool{
    pthread_t* threads_queue;
    int max_threads;
    int thread_front;
    int thread_rear;
    
    
    struct Func* tasks;
    int max_tasks;
    int task_front;
    int task_rear;
};


int ThreadPool_init(struct ThreadPool* pool,int maxThreads, int maxTasks){

    pool->threads_queue = (pthread_t*)malloc( (maxThreads + 1) * sizeof(pthread_t));
    pool->tasks = (struct Func*)malloc((maxTasks + 1) * sizeof(struct Func) );

    if(!pool->thread_front || !pool->tasks){
        return 1;
    }

    pool->max_tasks = maxTasks;
    pool->max_threads = maxThreads;

    pool->task_front = pool->task_rear = pool->thread_front = pool->thread_rear = 0;


}

int ThreadPool_threads_full(struct ThreadPool* pool){
    return (pool->thread_rear + 1) % (pool->max_threads) == (pool->thread_front);
}

int ThreadPool_threads_empty(struct ThreadPool* pool){
    return (pool->thread_rear == pool->thread_front);
}

int ThreadPool_threads_push(struct ThreadPool* pool, func runner, void* arg){
    if(ThreadPool_threads_full(pool)) return 1;
    int ret = pthread_create(&(pool->threads_queue[pool->thread_rear]), NULL,runner, arg);
    pool->thread_rear = (pool->thread_rear + 1 ) % pool->max_threads;
    return ret;
}

int ThreadPool_threads_pop(struct ThreadPool* pool){
    if(ThreadPool_threads_empty(pool)) return 0;
    pool->thread_front = (pool->thread_front + 1 ) % pool->max_threads;
}
int ThreadPool_threads_popv2(struct ThreadPool* pool){
    if(ThreadPool_threads_empty(pool)) return 0;
    (pool->threads_queue)[pool->thread_front] = 0;
    pool->thread_front = (pool->thread_front + 1 ) % pool->max_threads;
}

pthread_t ThreadPool_threads_gettop(struct ThreadPool* pool){
    return (pool->threads_queue)[pool->thread_front];
}

int ThreadPool_threads_cancel_top(struct ThreadPool* pool){
    if(ThreadPool_threads_empty(pool)) return 1;
    int ret = pthread_cancel((pool->threads_queue[pool->thread_front]));
    pool->thread_front = (pool->thread_front  + 1) % pool->max_threads;
    return ret;
}

int ThreadPool_tasks_full(struct ThreadPool* pool){
    return (pool->task_rear + 1) % (pool->max_tasks) == (pool->task_front);
}
int ThreadPool_tasks_empty(struct ThreadPool* pool){
    return (pool->task_rear == pool->task_front);
}

int ThreadPool_tasks_push(struct ThreadPool* pool, func runner, void* arg){
    if(ThreadPool_tasks_full(pool)) return 1;
    
    ((pool->tasks)[pool->thread_rear]).arg = arg;
    ((pool->tasks)[pool->thread_rear]).runner = runner;
    pool->task_rear = (pool->task_rear + 1 ) % pool->max_tasks;

}

int ThreadPool_tasks_pop(struct ThreadPool* pool){
    if(ThreadPool_tasks_empty(pool)) return 0;
    pool->task_front = (pool->task_front + 1 ) % pool->max_tasks;
}

struct Func ThreadPool_tasks_gettop(struct ThreadPool* pool){
    return (pool->tasks)[pool->task_front];
}

int ThreadPool_push(struct ThreadPool* pool, func runner, void* arg){

    if(ThreadPool_threads_full(pool)){
        if (ThreadPool_tasks_full(pool)){
            return 1;
        }
        ThreadPool_tasks_push(pool, runner, arg);
    }else{
        ThreadPool_threads_push(pool, runner, arg);
    }

}

int ThreadPool_print(struct ThreadPool* pool,int num){

    for (int i = 0; i < num ; ++ i){
        printf("thread %ld\n", (pool->threads_queue)[i]);
    }

}

int ThreadPool_threads_run(struct ThreadPool* pool){

    while(!ThreadPool_threads_empty(pool)){
        pthread_t temp = ThreadPool_threads_gettop(pool);
        pthread_join(temp, NULL);
        printf("thread:%ld ", temp);
        ThreadPool_threads_pop(pool);
    }
    
}
int ThreadPool_run(struct ThreadPool* pool){

    while(!ThreadPool_threads_empty(pool)){
        pthread_join(ThreadPool_threads_gettop(pool), NULL);
        ThreadPool_threads_pop(pool);
    }

    ThreadPool_print(pool, 10);

    // while(!ThreadPool_tasks_empty(pool)){
    //     struct Func temp = ThreadPool_tasks_gettop(pool);
    //     ThreadPool_threads_push(pool, temp.runner, temp.arg);
    //     ThreadPool_tasks_pop(pool);
    // }

    // ThreadPool_print(pool, 10);

    // while(!ThreadPool_threads_empty(pool)){
    //     pthread_join(ThreadPool_threads_gettop(pool), NULL);
    //     ThreadPool_threads_popv2(pool);
    // }

}

static void *runner(void *param)
{
    int randnum = rand() % 20;
    printf("id:%ld  randnum:%d\n", pthread_self(), randnum);
    pthread_exit(0);

}


int main(){

    struct ThreadPool pool;
    ThreadPool_init(&pool, 11, 11);
    
    for(int i = 0 ; i < 11 ; ++ i){
        ThreadPool_push(&pool, runner, NULL);
    }

    ThreadPool_print(&pool, 10);

    printf("rear:%d front:%d \n", pool.thread_rear, pool.thread_front);

    ThreadPool_run(&pool);
    ThreadPool_run(&pool);

}